#define IMGUI_IMPLEMENTATION
#include <stdio.h>
#include "glad/glad.h"
#include <GLFW/glfw3.h>
#include <opencv2/opencv.hpp>
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"

const auto vshader_source =
    "#version 400\n"
    "layout (location = 0) in vec2 pos;\n"
    "layout (location = 1) in vec2 coord;\n"
    "out vec2 textCoord;\n"
    "void main() {\n"
    "    gl_Position = vec4(pos, 0.0, 1.0);\n"
    "    textCoord = coord;\n"
    "}";
const auto fshader_source =
    "#version 400\n"
    "in vec2 textCoord;\n"
    "out vec4 color;\n"
    "uniform sampler2D text;\n"
    "void main() {\n"
    "    color = texture(text, textCoord);\n"
    "}";
float verts[] = {
    // 1-2 坐标 3-4 纹理坐标
    -1.0, -1.0, 0.0, 1.0, //
    -1.0, 1.0, 0.0, 0.0,  //
    1.0, 1.0, 1.0, 0.0,   //
    1.0, -1.0, 1.0, 1.0   //
};

constexpr const unsigned int CAM_MAX_COUNT = 4;

int main()
{
    cv::Mat mat;
    cv::VideoCapture camera(0);
    auto width = camera.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH);
    auto height = camera.get(cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT);
    if (!camera.isOpened())
    {
        width = 1280;
        height = 960;
    }
    if (!glfwInit())
    {
        fprintf(stderr, "Error: Failed to initialize GLFW library ...\n");
    }
    auto window = glfwCreateWindow(width, height, "CameraGL", nullptr, nullptr);
    if (!window)
    {
        fprintf(stderr, "Error: Failed to create GLFW window ...\n");
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSwapInterval(0);
    glfwSetFramebufferSizeCallback(window, [](GLFWwindow *, int w, int h)
                                   { glViewport(0, 0, w, h); });
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        fprintf(stderr, "Error: Failed to initialize GLAD ...\n");
        return -1;
    }

    ImGui::CreateContext();
    ImGui_ImplOpenGL3_Init();
    ImGui_ImplGlfw_InitForOpenGL(window, true);

    auto &IO = ImGui::GetIO();
    {
        auto pf = fopen("Ubuntu-M.ttf", "rb");
        if (!pf)
        {
            fprintf(stderr, "Error: Unable to find font file \"DroidSans.ttf\" ...\n");
            return -1;
        }
    }
    IO.Fonts->AddFontFromFileTTF("Ubuntu-M.ttf", 16.0);
    ImGui::StyleColorsLight();

    GLuint VBO, ImgText;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glGenTextures(1, &ImgText);
    glBindTexture(GL_TEXTURE_2D, ImgText);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    GLuint vshader, fshader, shader;
    vshader = glCreateShader(GL_VERTEX_SHADER);
    fshader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(vshader, 1, &vshader_source, nullptr);
    glShaderSource(fshader, 1, &fshader_source, nullptr);
    glCompileShader(vshader);
    glCompileShader(fshader);
    shader = glCreateProgram();
    glAttachShader(shader, vshader);
    glAttachShader(shader, fshader);
    glLinkProgram(shader);
    glUseProgram(shader);
    glDeleteShader(vshader);
    glDeleteShader(fshader);

    while (!glfwWindowShouldClose(window))
    {
        static int current_cam = 0, prev_cam = 0;
        static const char *const list_items[] = {
            "Camera 0", "Camera 1", "Camera 2", "Camera 3" // Cameras
        };
        static char fps_text[16];
        static char img_path[512];
        glClear(GL_COLOR_BUFFER_BIT);
        glClearColor(0.0, 0.0, 0.0, 1.0);

        camera >> mat;
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mat.cols, mat.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, mat.data);
        glDrawArrays(GL_POLYGON, 0, 4);

        ImGui_ImplGlfw_NewFrame();
        ImGui_ImplOpenGL3_NewFrame();
        ImGui::NewFrame();
        ImGui::Begin("CameraGL", nullptr);
        snprintf(fps_text, 16, "FPS: %d", (int)IO.Framerate);
        ImGui::ListBox(" ", &current_cam, list_items, CAM_MAX_COUNT);
        ImGui::InputText("Output image ", img_path, 512);
        ImGui::Text("FPS: %d    ", (int)IO.Framerate);
        ImGui::SameLine();
        if (ImGui::Button("Capture!  ") && strlen(img_path) > 0)
        {
            cv::imwrite(img_path, mat);
        }
        ImGui::SameLine();
        if (ImGui::Button("Quit "))
        {
            glfwSetWindowShouldClose(window, true);
        }
        if (current_cam != prev_cam)
        {
            camera.open(current_cam);
            prev_cam = current_cam;
        }
        ImGui::End();
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();
    return 0;
}